package org.example;

import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * author: zhanggw
 * 创建时间:  2022/3/25
 */
@Slf4j
@SpringBootTest
public class AppiumTest2 {
    @Resource
    private UiAutomator2Options options;
    @Test
    public void test() throws InterruptedException {
//        // 初始化UiAutomator2Options
//        UiAutomator2Options options = new UiAutomator2Options();
//
//        // 设置平台名称
//        options.setPlatformName("Android");
//        // 设置平台版本
//        options.setPlatformVersion("7");
//        // 设置设备名称
//        options.setDeviceName("1");
//
//        // 设置APP信息
//        options.setAppPackage("com.android.browser");
//        options.setAppActivity("com.android.browser.BrowserActivity");

        // 创建AppiumDriver对象，连接上Appium服务器，并将相应的键值对传过去
        AndroidDriver driver = null;

        try {
            driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), options);

//            Thread.sleep(3000);

            driver.findElement(By.id("com.android.browser:id/url")).click();
            driver.findElement(By.className("android.widget.EditText")).sendKeys("https://yjeju.rq9eyvt.store/s97b1gyl?_360safeparam=29903015");

            // 使用 Actions 类发送回车
            Actions actions = new Actions(driver);
            actions.sendKeys(Keys.ENTER).perform();

            // 使用显式等待来定位并点击元素
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 最长等待时间为10秒
            wait.until(
                    ExpectedConditions.elementToBeClickable(By.xpath("//*[@text='安装']")) // 请根据实际情况调整元素类型
            ).click();


            // 监控下载目录，获取最新下载的文件
            String downloadDir = "/sdcard/Download";
            Thread.sleep(5000);
            String downloadedFile = waitForDownloadToComplete(downloadDir);

            if (downloadedFile != null) {
                // 复制文件到本地
                String localFilePath = "D:\\dcloud-software\\software\\static\\" + downloadedFile;
                copyFileFromEmulatorToLocal(downloadDir + "/" + downloadedFile, localFilePath);
            } else {
                log.error("下载文件未完成或未找到！");
            }

        } catch (Exception e) {
            log.error("Appium异常, 测试用例不通过!", e);
            throw new RuntimeException(e);
        }
//        finally {
//            if (driver != null) {
//                driver.quit();
//            }
//        }
    }

    /**
     * 等待下载到指定目录的文件完成。
     * 通过检查文件是否被锁定来判断下载是否完成。
     * 如果文件在最大重试次数内未解锁，则认为下载失败。
     *
     * @param downloadDir 下载文件的目标目录。
     * @return 完成下载的文件名，如果下载失败则返回null。
     * @throws InterruptedException 如果线程被中断。
     */
    // 定时检查下载目录中的文件，直到文件下载完成
    private static String waitForDownloadToComplete(String downloadDir) throws InterruptedException {
        // 初始化重试计数和最大重试次数
        int retryCount = 0;
        int maxRetries = 100; // 最大重试次数
        int retryInterval = 5000; // 每次重试间隔，单位：毫秒

        List<String> files = null;
        String currentFileName = null;
        try {
            // 列出目录中最近修改的文件列表
            //获得文件列表
            files = listFilesInDirectoryByTime(downloadDir);
            for (String file : files) {
                log.info("列表文件名：{}", file);
            }
            // 检查文件列表是否为空，抛出异常以避免后续操作
            if (files.get(1) == null) {
                throw new RuntimeException("空指针异常");
            }
            // 获取第二个文件名，假设它是正在下载的文件
            currentFileName = files.get(1);
            log.info("正在下载文件名：{}", currentFileName);
        } catch (IOException e) {
            log.error("列出目录文件时出错", e);
        }

        // 循环检查文件是否解锁，直到重试次数达到最大值
        while (retryCount < maxRetries) {
            // 如果文件未被锁定，则下载完成
            if (!isFileLocked(downloadDir + "/" + currentFileName, currentFileName)) {
                log.error("文件下载完成");
                return currentFileName;
            } else {
                // 如果文件被锁定，重试计数增加，并等待一段时间后再次检查
                retryCount++;
                Thread.sleep(retryInterval);
                log.error("文件下载未完成");
            }
        }
        // 如果达到最大重试次数仍未下载完成，则返回null
        return null;
    }

    /**
     * 列出指定目录中的文件，按修改时间排序。
     * 通过执行ADB shell命令来获取文件列表。
     *
     * @param directory 目标目录。
     * @return 目录中文件的列表。
     * @throws IOException 如果执行ADB命令时发生错误。
     */
    private static List<String> listFilesInDirectoryByTime(String directory) throws IOException {
        // 执行ADB shell ls命令来列出目录中的文件
        Process process = Runtime.getRuntime().exec(new String[]{"adb", "shell", "ls", "-lt", directory});
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        List<String> files = new ArrayList<>();
        while ((line = reader.readLine()) != null) {
            // 解析命令输出，获取文件名
            // 解析行，获取文件名
            String[] parts = line.split("\\s+");
            if (parts.length > 0) {
                String fileName = parts[parts.length - 1];
                files.add(fileName);
            }
        }
        reader.close();
        return files;
    }

    /**
     * 从模拟器中复制文件到本地路径。
     * 使用ADB pull命令执行文件复制。
     *
     * @param remoteFilePath 模拟器中的文件路径。
     * @param localFilePath  本地目标文件路径。
     */
    private static void copyFileFromEmulatorToLocal(String remoteFilePath, String localFilePath) {
        try {
            // 执行ADB pull命令来复制文件
            String[] command = {"adb", "pull", remoteFilePath, localFilePath};
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            process.waitFor();
            reader.close();
        } catch (Exception e) {
            log.error("复制文件失败!", e);
        }
    }


    /**
     * 检查指定路径的文件是否被锁定。
     * 通过执行ADB shell lsof命令来检查文件是否被锁定。
     *
     * @param filePath 文件的完整路径。
     * @param fileName 文件名。
     * @return 如果文件被锁定，则返回true；否则返回false。
     */
    private static boolean isFileLocked(String filePath, String fileName) {
        Process process;
        try {
            // 执行ADB shell lsof命令来检查文件是否被锁定
            process = Runtime.getRuntime().exec(new String[]{"adb", "shell", "lsof", filePath});
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                // 如果命令输出中包含文件名，则文件被锁定
                System.out.println("hhhhh" + line);
                if (line.contains(fileName)) {
                    return true; // 文件被锁定
                }
            }
        } catch (IOException e) {
            log.error("检查文件锁定状态时出错", e);
        }
        return false;
    }

}